package org.hyperion.rs2.task.impl;

import java.util.Iterator;

import org.hyperion.rs2.GameEngine;
import org.hyperion.rs2.content.minigames.FightCaves;
import org.hyperion.rs2.model.Appearance;
import org.hyperion.rs2.model.ChatMessage;
import org.hyperion.rs2.model.Entity;
import org.hyperion.rs2.model.Item;
import org.hyperion.rs2.model.Location;
import org.hyperion.rs2.model.Player;
import org.hyperion.rs2.model.UpdateFlags;
import org.hyperion.rs2.model.World;
import org.hyperion.rs2.model.UpdateFlags.UpdateFlag;
import org.hyperion.rs2.model.container.Container;
import org.hyperion.rs2.model.container.Equipment;
import org.hyperion.rs2.model.container.Equipment.EquipmentType;
import org.hyperion.rs2.net.Packet;
import org.hyperion.rs2.net.PacketBuilder;
import org.hyperion.rs2.task.Task;
import org.hyperion.rs2.util.TextUtils;

/**
 * A task which creates and sends the player update block.
 * 
 * @author Graham Edgecombe
 * 
 */
public class PlayerUpdateTask implements Task {

	/**
	 * The player.
	 */
	private Player player;

	/**
	 * Creates an update task.
	 * 
	 * @param player
	 *            The player.
	 */
	public PlayerUpdateTask(Player player) {
		this.player = player;
	}

	@Override
	public void execute(GameEngine context) {
		/*
		 * If the map region changed send the new one. We do this immediately as
		 * the client can begin loading it before the actual packet is received.
		 */
		if (player.isMapRegionChanging()) {
			player.getActionSender().sendMapRegion();
		}

		/*
		 * The update block packet holds update blocks and is send after the
		 * main packet.
		 */
		PacketBuilder updateBlock = new PacketBuilder();

		/*
		 * The main packet is written in bits instead of bytes and holds
		 * information about the local list, players to add and remove, movement
		 * and which updates are required.
		 */
		PacketBuilder packet = new PacketBuilder(42, Packet.Type.VARIABLE_SHORT);
		packet.startBitAccess();

		/*
		 * Updates this player.
		 */
		updateThisPlayerMovement(packet);
		updatePlayer(updateBlock, player, false, false);

		/*
		 * Write the current size of the player list.
		 */
		packet.putBits(8, player.getLocalPlayers().size());

		/*
		 * Iterate through the local player list.
		 */
		for (Iterator<Player> it$ = player.getLocalPlayers().iterator(); it$
				.hasNext();) {
			/*
			 * Get the next player.
			 */
			Player otherPlayer = it$.next();

			/*
			 * If the player should still be in our list.
			 */
			if (World.getWorld().getPlayers().contains(otherPlayer)
					&& !otherPlayer.isTeleporting()
					&& otherPlayer.getLocation().isWithinDistance(
							player.getLocation())
					&& !FightCaves.IN_CAVES.contains(otherPlayer)) {
				/*
				 * Update the movement.
				 */
				updatePlayerMovement(packet, otherPlayer);

				/*
				 * Check if an update is required, and if so, send the update.
				 */
				if (otherPlayer.getUpdateFlags().isUpdateRequired()) {
					updatePlayer(updateBlock, otherPlayer, false, false);
				}
			} else {
				/*
				 * Otherwise, remove the player from the list.
				 */
				it$.remove();

				/*
				 * Tell the client to remove the player from the list.
				 */
				packet.putBits(1, 1);
				packet.putBits(2, 3);
			}
		}

		/*
		 * Loop through every player.
		 */
		for (Player otherPlayer : World.getWorld().getRegionManager()
				.getLocalPlayers(player)) {
			/*
			 * Check if there is room left in the local list.
			 */
			if (player.getLocalPlayers().size() >= 255) {
				/*
				 * There is no more room left in the local list. We cannot add
				 * more players, so we just ignore the extra ones. They will be
				 * added as other players get removed.
				 */
				break;
			}

			/*
			 * If they should not be added ignore them.
			 */
			if (otherPlayer == player
					|| player.getLocalPlayers().contains(otherPlayer)
					|| FightCaves.IN_CAVES.contains(otherPlayer)) {
				continue;
			}

			/*
			 * Add the player to the local list if it is within distance.
			 */
			player.getLocalPlayers().add(otherPlayer);

			/*
			 * Add the player in the packet.
			 */
			addNewPlayer(packet, otherPlayer);

			/*
			 * Update the player, forcing the appearance flag.
			 */
			updatePlayer(updateBlock, otherPlayer, true, false);
		}

		/*
		 * Check if the update block is not empty.
		 */
		if (!updateBlock.isEmpty()) {
			/*
			 * Write a magic id indicating an update block follows.
			 */
			packet.putBits(11, 2047);
			packet.finishBitAccess();

			/*
			 * Add the update block at the end of this packet.
			 */
			packet.put(updateBlock.toPacket().getPayload());
		} else {
			/*
			 * Terminate the packet normally.
			 */
			packet.finishBitAccess();
		}

		/*
		 * Write the packet.
		 */
		player.write(packet.toPacket());
	}

	/**
	 * Updates a non-this player's movement.
	 * 
	 * @param packet
	 *            The packet.
	 * @param otherPlayer
	 *            The player.
	 */
	public void updatePlayerMovement(PacketBuilder packet, Player otherPlayer) {
		/*
		 * Check which type of movement took place.
		 */
		if (otherPlayer.getSprites().getPrimarySprite() == -1) {
			/*
			 * If no movement did, check if an update is required.
			 */
			if (otherPlayer.getUpdateFlags().isUpdateRequired()) {
				/*
				 * Signify that an update happened.
				 */
				packet.putBits(1, 1);

				/*
				 * Signify that there was no movement.
				 */
				packet.putBits(2, 0);
			} else {
				/*
				 * Signify that nothing changed.
				 */
				packet.putBits(1, 0);
			}
		} else if (otherPlayer.getSprites().getSecondarySprite() == -1) {
			/*
			 * The player moved but didn't run. Signify that an update is
			 * required.
			 */
			packet.putBits(1, 1);

			/*
			 * Signify we moved one tile.
			 */
			packet.putBits(2, 1);

			/*
			 * Write the primary sprite (i.e. walk direction).
			 */
			packet.putBits(3, otherPlayer.getSprites().getPrimarySprite());

			/*
			 * Write a flag indicating if a block update happened.
			 */
			packet.putBits(1,
					otherPlayer.getUpdateFlags().isUpdateRequired() ? 1 : 0);
		} else {
			/*
			 * The player ran. Signify that an update happened.
			 */
			packet.putBits(1, 1);

			/*
			 * Signify that we moved two tiles.
			 */
			packet.putBits(2, 2);

			/*
			 * Write the primary sprite (i.e. walk direction).
			 */
			packet.putBits(3, otherPlayer.getSprites().getPrimarySprite());

			/*
			 * Write the secondary sprite (i.e. run direction).
			 */
			packet.putBits(3, otherPlayer.getSprites().getSecondarySprite());

			/*
			 * Write a flag indicating if a block update happened.
			 */
			packet.putBits(1,
					otherPlayer.getUpdateFlags().isUpdateRequired() ? 1 : 0);
		}
	}

	/**
	 * Adds a new player.
	 * 
	 * @param packet
	 *            The packet.
	 * @param otherPlayer
	 *            The player.
	 */
	public void addNewPlayer(PacketBuilder packet, Player otherPlayer) {
		/*
		 * Write the player index.
		 */
		packet.putBits(11, otherPlayer.getIndex());

		/*
		 * Write two flags here: the first indicates an update is required (this
		 * is always true as we add the appearance after adding a player) and
		 * the second to indicate we should discard client-side walk queues.
		 */
		packet.putBits(1, 1);
		packet.putBits(1, 1);
		packet.putBits(3, 1);

		/*
		 * Calculate the x and y offsets.
		 */
		int yPos = otherPlayer.getLocation().getY()
				- player.getLocation().getY();
		int xPos = otherPlayer.getLocation().getX()
				- player.getLocation().getX();

		/*
		 * Write the x and y offsets.
		 */
		packet.putBits(5, xPos);
		packet.putBits(5, yPos);

	}

	private static void appendHit2Update(final Player p,
			final PacketBuilder updateBlock) {
		updateBlock.putByteC(p.getDamage().getHitDamage2());
		updateBlock.putByteC(p.getDamage().getHitType2());
		updateBlock.putByteS((byte) p.getSkills().getLevel(3));
		updateBlock.put((byte) p.getSkills().getLevelForExperience(3));
	}

	private static void appendHitUpdate(final Player p,
			final PacketBuilder updateBlock) {
		updateBlock.putByteA(p.getDamage().getHitDamage1());
		updateBlock.putByteA(p.getDamage().getHitType1());
		updateBlock.putByteC(p.getSkills().getLevel(3));
		updateBlock.putByteC(p.getSkills().getLevelForExperience(3));
	}

	/**
	 * Updates a player.
	 * 
	 * @param packet
	 *            The packet.
	 * @param otherPlayer
	 *            The other player.
	 * @param forceAppearance
	 *            The force appearance flag.
	 * @param noChat
	 *            Indicates chat should not be relayed to this player.
	 */
	public void updatePlayer(PacketBuilder packet, Player otherPlayer,
			boolean forceAppearance, boolean noChat) {
		/*
		 * If no update is required and we don't have to force an appearance
		 * update, don't write anything.
		 */
		if (!otherPlayer.getUpdateFlags().isUpdateRequired()
				&& !forceAppearance) {
			return;
		}

		/*
		 * We can used the cached update block!
		 */
		synchronized (otherPlayer) {
			if (otherPlayer.hasCachedUpdateBlock() && otherPlayer != player
					&& !forceAppearance && !noChat) {
				packet.put(otherPlayer.getCachedUpdateBlock().getPayload()
						.flip());
				return;
			}

			/*
			 * We have to construct and cache our own block.
			 */
			PacketBuilder block = new PacketBuilder();

			/*
			 * Calculate the bitmask.
			 */
			int mask = 0;
			final UpdateFlags flags = otherPlayer.getUpdateFlags();

			if (flags.get(UpdateFlag.FACE_COORDINATE)) {
				mask |= 0x80;
			}
			if (flags.get(UpdateFlag.HIT)) {
				mask |= 0x200;
			}
			if (flags.get(UpdateFlag.FORCED_CHAT)) {
				mask |= 1;
			}
			if (flags.get(UpdateFlag.CHAT) && !noChat) {
				mask |= 4;
			}
			if (flags.get(UpdateFlag.HIT_2)) {
				mask |= 0x20;
			}
			if (flags.get(UpdateFlag.FACE_ENTITY)) {
				mask |= 0x10;
			}
			if (flags.get(UpdateFlag.ANIMATION)) {
				mask |= 0x40;
			}
			if (flags.get(UpdateFlag.GRAPHICS)) {
				mask |= 0x100;
			}
			if (flags.get(UpdateFlag.APPEARANCE) || forceAppearance) {
				mask |= 8;
			}
			/*
			 * if(flags.get(UpdateFlag.FORCE_MOVEMENT)) { mask |= 0x400; }
			 */

			/*
			 * Check if the bitmask would overflow a byte.
			 */
			if (mask >= 2) {
				/*
				 * Write it as a short and indicate we have done so.
				 */
				mask |= 2;
				block.put((byte) (mask & 0xFF));
				block.put((byte) (mask >> 8));
			} else {
				/*
				 * Write it as a byte.
				 */
				block.put((byte) (mask));
			}
			/*
			 * Append the appropriate updates.
			 */
			if (flags.get(UpdateFlag.FACE_COORDINATE)) {
				Location loc = otherPlayer.getFaceLocation();
				if (loc == null) {
					block.putShortA(0);
					block.putShortA(0);
				} else {
					block.putShortA(2 * loc.getX() + 1);
					block.putShortA(2 * loc.getY() + 1);
				}
			}
			if (flags.get(UpdateFlag.HIT)) {
				appendHitUpdate(otherPlayer, block);
			}
			if (flags.get(UpdateFlag.FORCED_CHAT)) {
				block.putString(otherPlayer.getForceChatText());
			}
			if (flags.get(UpdateFlag.CHAT) && !noChat) {
				appendChatUpdate(block, otherPlayer);
			}
			if (flags.get(UpdateFlag.HIT_2)) {
				appendHit2Update(otherPlayer, block);
			}
			if (flags.get(UpdateFlag.FACE_ENTITY)) {
				Entity entity = otherPlayer.getInteractingEntity();
				block.putLEShortA(entity == null ? -1 : entity.getClientIndex());
			}
			if (flags.get(UpdateFlag.ANIMATION)) {
				appendAnimationUpdate(block, otherPlayer);
			}
			if (flags.get(UpdateFlag.GRAPHICS)) {
				appendGraphicsUpdate(block, otherPlayer);
			}
			if (flags.get(UpdateFlag.APPEARANCE) || forceAppearance) {
				appendPlayerAppearanceUpdate(block, otherPlayer);
			}

			/*
			 * Convert the block builder to a packet.
			 */
			Packet blockPacket = block.toPacket();

			/*
			 * Now it is over, cache the block if we can.
			 */
			if (otherPlayer != player && !forceAppearance && !noChat) {
				otherPlayer.setCachedUpdateBlock(blockPacket);
			}

			/*
			 * And finally append the block at the end.
			 */
			packet.put(blockPacket.getPayload());
		}
	}

	/*
	 * Force movement update mask (0x400) - Yesterday, 12:58 AM Thanks to Blake
	 * for making the topic which got me curious about this...it's the force
	 * movement mask (0x400 - the only one left for 317 RSPS to get).
	 * 
	 * This is the first update mask in the list, so it goes at the top (look
	 * how your others are done).
	 * 
	 * Code:
	 * 
	 * public void appendForceMovement(stream str) { str.writeByteS(x1);
	 * str.writeByteS(y1); str.writeByteS(x2); str.writeByteS(y2);
	 * str.writeWordBigEndianA(speed1); str.writeWordA(speed2);
	 * str.writeByteS(direction); }
	 * 
	 * Now i will explain the variables.
	 * 
	 * As you can see, there are 2 sets of coordinate variables, x1/y1 and
	 * x2,y2, these refer to region coordinates (minimum of 0, maximum of 104),
	 * named currentX and currentY in any winterLove server.
	 * 
	 * x1 and y1 is the region x+y you will start from, if you are not at these
	 * coordinates, it will move to there (it will walk there, and will noclip
	 * if need be).
	 * 
	 * x2 and y2 is the region x+y you will finish at, your character will move
	 * from x1,y1 to x2,y2.
	 * 
	 * So lets use this as an example. Code:
	 * 
	 * currentX = 50; // X where we are standing currentY = 50; // Y where we
	 * are standing x1 = currentX; // Start from our current position y1 =
	 * currentY; // Start from our current position x2 = currentX + 4; // Move 4
	 * squares east. y2 = currentY // stay in the same Y coordinate (we won't
	 * move north or south);
	 * 
	 * So now the mask knows that we want to move 4 squares east, i will explain
	 * the speed variables now.
	 * 
	 * speed1 is the speed you will move from your current coordinates to x1/y1
	 * we specified earlier.
	 * 
	 * speed2, if you haven't guessed yet, is the same as above, but the speed
	 * you will move from x1/y1 to x2/y2.
	 * 
	 * NOTE : For speed, 0 is instant, 1000 is dirt slow (so it's the opposite
	 * of real speed), the speeds cannot be the same or the client crashes .
	 * 
	 * The direction variable should be self explanatory, it's the direction
	 * your character faces, it can be between 0 to 3 (inclusive)..but i didn't
	 * test those yet
	 */

	/**
	 * Appends an animation update.
	 * 
	 * @param block
	 *            The update block.
	 * @param otherPlayer
	 *            The player.
	 */
	private void appendAnimationUpdate(PacketBuilder block, Player otherPlayer) {
		block.putShort(otherPlayer.getCurrentAnimation().getId());
		block.putByteS((byte) otherPlayer.getCurrentAnimation().getDelay());
	}

	/**
	 * Appends a graphics update.
	 * 
	 * @param block
	 *            The update block.
	 * @param otherPlayer
	 *            The player.
	 */
	private void appendGraphicsUpdate(PacketBuilder block, Player otherPlayer) {
		block.putLEShort(otherPlayer.getCurrentGraphic().getId());
		block.putInt2(otherPlayer.getCurrentGraphic().getDelay());
	}

	/**
	 * Appends a chat text update.
	 * 
	 * @param packet
	 *            The packet.
	 * @param otherPlayer
	 *            The player.
	 */
	private void appendChatUpdate(PacketBuilder block, Player otherPlayer) {
		/*
		 * ChatMessage cm = otherPlayer.getCurrentChatMessage();
		 * 
		 * byte[] bytes = cm.getText();
		 * 
		 * packet.putLEShort(((cm.getColour() & 0xFF) << 8) | (cm.getEffects() &
		 * 0xFF)); packet.put((byte) otherPlayer.getRights().toInteger());
		 * packet.putByteC(bytes.length); for(int ptr = bytes.length -1; ptr >=
		 * 0; ptr--) { packet.put(bytes[ptr]); }
		 */
		ChatMessage cm = otherPlayer.getCurrentChatMessage();
		block.putShortA(cm.getEffects());
		block.putByteC(otherPlayer.getRights().toInteger());
		byte[] chatStr = new byte[256];
		chatStr[0] = (byte) cm.getChatText().length();
		int offset = 1 + TextUtils.encryptPlayerChat(chatStr, 0, 1, cm
				.getChatText().length(), cm.getChatText().getBytes());
		block.put((byte) offset);
		block.putBytesA(chatStr, 0, offset);
	}

	/**
	 * Appends an appearance update.
	 * 
	 * @param packet
	 *            The packet.
	 * @param otherPlayer
	 *            The player.
	 */
	private void appendPlayerAppearanceUpdate(PacketBuilder packet,
			Player otherPlayer) {
		Appearance app = otherPlayer.getAppearance();
		Container eq = otherPlayer.getEquipment();

		PacketBuilder playerProps = new PacketBuilder();
		playerProps.put((byte) app.getGender()); // gender
		playerProps.put((byte) otherPlayer.getSkulls().getSkullId()); // skull
																		// icon
		playerProps.put((byte) otherPlayer.getPrayer().getPrayerIcon()); // prayer
																			// icon

		for (int i = 0; i < 4; i++) {
			if (eq.isSlotUsed(i)) {
				playerProps.putShort((short) 512 + eq.get(i).getId());
			} else {
				playerProps.put((byte) 0);
			}
		}
		if (eq.isSlotUsed(Equipment.SLOT_CHEST)) {
			playerProps.putShort((short) 512
					+ eq.get(Equipment.SLOT_CHEST).getId());
		} else {
			playerProps.putShort((short) 0x100 + app.getChest()); // chest
		}
		if (eq.isSlotUsed(Equipment.SLOT_SHIELD)) {
			playerProps.putShort((short) 512
					+ eq.get(Equipment.SLOT_SHIELD).getId());
		} else {
			playerProps.put((byte) 0);
		}
		Item chest = eq.get(Equipment.SLOT_CHEST);
		if (chest != null) {
			if (!Equipment.is(EquipmentType.PLATEBODY, chest)) {
				playerProps.putShort((short) 0x100 + app.getArms());
			} else {
				playerProps.putShort((short) 512 + chest.getId());
			}
		} else {
			playerProps.putShort((short) 0x100 + app.getArms());
		}
		if (eq.isSlotUsed(Equipment.SLOT_BOTTOMS)) {
			playerProps.putShort((short) 512
					+ eq.get(Equipment.SLOT_BOTTOMS).getId());
		} else {
			playerProps.putShort((short) 0x100 + app.getLegs());
		}
		Item helm = eq.get(Equipment.SLOT_HELM);
		if (helm != null) {
			if (!Equipment.is(EquipmentType.FULL_HELM, helm)
					&& !Equipment.is(EquipmentType.FULL_MASK, helm)) {
				playerProps.putShort((short) 0x100 + app.getHead());
			} else {
				playerProps.put((byte) 0);
			}
		} else {
			playerProps.putShort((short) 0x100 + app.getHead());
		}
		if (eq.isSlotUsed(Equipment.SLOT_GLOVES)) {
			playerProps.putShort((short) 512
					+ eq.get(Equipment.SLOT_GLOVES).getId());
		} else {
			playerProps.putShort((short) 0x100 + app.getHands());
		}
		if (eq.isSlotUsed(Equipment.SLOT_BOOTS)) {
			playerProps.putShort((short) 512
					+ eq.get(Equipment.SLOT_BOOTS).getId());
		} else {
			playerProps.putShort((short) 0x100 + app.getFeet());
		}
		boolean fullHelm = false;
		if (helm != null) {
			fullHelm = !Equipment.is(EquipmentType.FULL_HELM, helm);
		}
		if (fullHelm || app.getGender() == 1) {
			playerProps.put((byte) 0);
		} else {
			playerProps.putShort((short) 0x100 + app.getBeard());
		}

		playerProps.put((byte) app.getHairColour()); // hairc
		playerProps.put((byte) app.getTorsoColour()); // torsoc
		playerProps.put((byte) app.getLegColour()); // legc
		playerProps.put((byte) app.getFeetColour()); // feetc
		playerProps.put((byte) app.getSkinColour()); // skinc

		Item weapon = eq.get(Equipment.SLOT_WEAPON);
		if (player.differentUpdateAnimations()) {
			playerProps.putShort((short) player
					.getTemporaryUpdatingAnimation(0)); // stand
			playerProps.putShort((short) player
					.getTemporaryUpdatingAnimation(1)); // stand turn
			playerProps.putShort((short) player
					.getTemporaryUpdatingAnimation(2)); // walk
			playerProps.putShort((short) player
					.getTemporaryUpdatingAnimation(3)); // turn 180
			playerProps.putShort((short) player
					.getTemporaryUpdatingAnimation(4)); // turn 90 cw
			playerProps.putShort((short) player
					.getTemporaryUpdatingAnimation(5)); // turn 90 ccw
			playerProps.putShort((short) player
					.getTemporaryUpdatingAnimation(6)); // run
		} else {
			playerProps.putShort((short) Equipment.getStandAnim(weapon)); // stand
			playerProps.putShort((short) 0x337); // stand turn
			playerProps.putShort((short) Equipment.getWalkAnim(weapon)); // walk
			playerProps.putShort((short) 0x334); // turn 180
			playerProps.putShort((short) 0x335); // turn 90 cw
			playerProps.putShort((short) 0x336); // turn 90 ccw
			playerProps.putShort((short) Equipment.getRunAnim(weapon)); // run
		}
		playerProps.putLong(otherPlayer.getNameAsLong()); // player name
		playerProps.put((byte) otherPlayer.getSkills().getCombatLevel()); // combat
																			// level
		playerProps.putShort(0); // (skill-level instead of combat-level)
									// otherPlayer.getSkills().getTotalLevel());
									// // total level

		Packet propsPacket = playerProps.toPacket();

		packet.put((byte) (propsPacket.getLength() & 0xff));
		packet.putBytesA(propsPacket.getPayload(), 0, propsPacket.getLength());
		/*
		 * packet.putByteC(propsPacket.getLength());
		 * packet.put(propsPacket.getPayload());
		 */
	}

	/**
	 * Updates this player's movement.
	 * 
	 * @param packet
	 *            The packet.
	 */
	public void updateThisPlayerMovement(PacketBuilder packet) {
		/*
		 * Check if the player is teleporting.
		 */
		if (player.isTeleporting() || player.isMapRegionChanging()) {
			/*
			 * They are, so an update is required.
			 */
			packet.putBits(1, 1);

			/*
			 * This value indicates the player teleported.
			 */
			packet.putBits(2, 3);

			/*
			 * This indicates that the client should discard the walking queue.
			 */
			packet.putBits(1, 1);

			/*
			 * This is the new player height.
			 */
			packet.putBits(2, player.getLocation().getZ());

			/*
			 * These are the positions.
			 */
			packet.putBits(7,
					player.getLocation().getLocalY(player.getLastKnownRegion()));
			packet.putBits(7,
					player.getLocation().getLocalX(player.getLastKnownRegion()));

			/*
			 * This flag indicates if an update block is appended.
			 */
			packet.putBits(1, player.getUpdateFlags().isUpdateRequired() ? 1
					: 0);
		} else {
			/*
			 * Otherwise, check if the player moved.
			 */
			if (player.getSprites().getPrimarySprite() == -1) {
				/*
				 * The player didn't move. Check if an update is required.
				 */
				if (player.getUpdateFlags().isUpdateRequired()) {
					/*
					 * Signifies an update is required.
					 */
					packet.putBits(1, 1);

					/*
					 * But signifies that we didn't move.
					 */
					packet.putBits(2, 0);
				} else {
					/*
					 * Signifies that nothing changed.
					 */
					packet.putBits(1, 0);
				}
			} else {
				/*
				 * Check if the player was running.
				 */
				if (player.getSprites().getSecondarySprite() == -1) {
					/*
					 * The player walked, an update is required.
					 */
					packet.putBits(1, 1);

					/*
					 * This indicates the player only walked.
					 */
					packet.putBits(2, 1);

					/*
					 * This is the player's walking direction.
					 */
					packet.putBits(3, player.getSprites().getPrimarySprite());

					/*
					 * This flag indicates an update block is appended.
					 */
					packet.putBits(1, player.getUpdateFlags()
							.isUpdateRequired() ? 1 : 0);
				} else {
					/*
					 * The player ran, so an update is required.
					 */
					packet.putBits(1, 1);

					/*
					 * This indicates the player ran.
					 */
					packet.putBits(2, 2);

					/*
					 * This is the walking direction.
					 */
					packet.putBits(3, player.getSprites().getPrimarySprite());

					/*
					 * And this is the running direction.
					 */
					packet.putBits(3, player.getSprites().getSecondarySprite());

					/*
					 * And this flag indicates an update block is appended.
					 */
					packet.putBits(1, player.getUpdateFlags()
							.isUpdateRequired() ? 1 : 0);
				}
			}
		}
	}

}
